﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace FimetContoDeposito.DAL
{
    public interface IReporitoryObject<Tkey>
    {
        Tkey Id {get; set;}
    }

    public interface IRepository<T, Tkey>
    {
        IQueryable<T> GetAll();
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
        T Get(Tkey i);
        void Save();
    }

    public class Repository<T, Tkey> : IRepository<T, Tkey>, IDisposable
        where T : class
        //where Context : DbContext, new ()
    {
        DbContext _entity = null;

        public Repository(DbContext context)
        {
            _entity = context;
        }

        public void Add(T entity)
        {
            _entity.Set<T>().Add(entity);
            _entity.Entry(entity).State = System.Data.Entity.EntityState.Added;
        }

        public void Delete(T entity)
        {
            _entity.Set<T>().Remove(entity);
            _entity.Entry(entity).State = System.Data.Entity.EntityState.Deleted;
        }

        public void Edit(T entity)
        {
            _entity.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        }

        public IQueryable<T> GetAll()
        {
            return _entity.Set<T>();
        }

        public IQueryable<T> GetAll(
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<T> Query = _entity.Set<T>();

            if (!string.IsNullOrEmpty(includeProperties))
            {
                foreach (string IncludeProperty in includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    Query = Query.Include(IncludeProperty);
                }
            }

            if (orderBy != null)
            {
                return orderBy(Query);
            }
            return Query;
        }

        public T Get(Tkey i)
        {
            return _entity.Set<T>().Find(i);
        }

        public virtual IQueryable<T> Get(
            Expression<Func<T, bool>> filter = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<T> Query = _entity.Set<T>();

            if (filter != null)
            {
                Query = Query.Where(filter);
            }

            if (!string.IsNullOrEmpty(includeProperties))
            {
                foreach (string IncludeProperty in includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    Query = Query.Include(IncludeProperty);
                }
            }

            if (orderBy != null)
            {
                return orderBy(Query);
            }
            return Query;
        }

        public void Save()
        {
            try
            {
                _entity.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                // Retrieve the error messages as a list of strings.
                var errorMessages = ex.EntityValidationErrors
                        .SelectMany(x => x.ValidationErrors)
                        .Select(x => x.ErrorMessage);

                // Join the list to a single string.
                var fullErrorMessage = string.Join("; ", errorMessages);

                // Combine the original exception message with the new one.
                var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);

                GrayParrot.Logging.Logger.For<T>().Error(exceptionMessage, ex);

                // Throw a new DbEntityValidationException with the improved exception message.
                throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
            }
            catch (DbUpdateException dbUpdateEx)
            {
                if (dbUpdateEx.InnerException != null
                        && dbUpdateEx.InnerException.InnerException != null)
                {
                    SqlException sqlException = dbUpdateEx.InnerException.InnerException as SqlException;
                    if (sqlException != null)
                    {
                        string msg = string.Empty;

                        switch (sqlException.Number)
                        {
                            case 2627:  // Unique constraint error
                                msg = "{0} già presente";
                                break;
                            case 547:   // Constraint check violation
                                msg = "{0} utilizzato";
                                break;
                            case 2601:  // Duplicated key row error
                                msg = "{0} duplicato";
                                break;
                            default:
                                // A custom exception of yours for other DB issues
                                throw;
                        }

                        throw new Exception(msg);   // A custom exception of yours for concurrency issues
                    }

                    throw;
                }
            }
            catch (Exception ex)
            {
                GrayParrot.Logging.Logger.For<T>().Error("Errore in Save", ex);
                throw ex;
            }
        }

        public void Dispose()
        {
            if (_entity != null)
            {
                _entity.Dispose();
            }
        }
    }
}
